import json
import time
import random
import multiprocessing
import requests

from selenium import webdriver
from selenium.webdriver.edge.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import (
    ElementClickInterceptedException,
    NoSuchElementException,
    TimeoutException
)
from retrying import retry


"""
仅仅作为测试和自我学习使用，请不要用于学术作弊或者其他违法行为。
作者: forrest
"""


# ---------------- 公共工具函数 ----------------

def safe_click(driver, element, timeout=3):
    """安全点击，处理遮挡/不可点击的情况"""
    try:
        WebDriverWait(driver, timeout).until_not(
            EC.presence_of_element_located((By.CLASS_NAME, 'layui-layer-shade'))
        )
        driver.execute_script(
            "arguments[0].scrollIntoView({block: 'center', inline: 'nearest'});", element
        )
        clickable_element = WebDriverWait(driver, timeout).until(
            EC.element_to_be_clickable(element)
        )
        clickable_element.click()
    except ElementClickInterceptedException:
        driver.execute_script("arguments[0].click();", element)
    except Exception as e:
        print(f"safe_click 出错: {e}")


@retry(stop_max_attempt_number=3, wait_fixed=2000)
def safe_submit(driver):
    """安全提交，失败会重试"""
    try:
        submit_btn = driver.find_element(By.CSS_SELECTOR, '#ctlNext')
        safe_click(driver, submit_btn)
    except Exception as e:
        print(f"提交时发生错误，重试中... ({e})")
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        raise


# ---------------- 题型函数（来自 wjx.py） ----------------

def single_choice(driver, num, weights):
    questions = driver.find_elements(By.CSS_SELECTOR, f'#div{num} > div.ui-controlgroup')
    for question in questions:
        options = question.find_elements(By.CSS_SELECTOR, '.ui-radio')
        if not options:
            continue
        if weights == -1 or len(weights) != len(options):
            weights = [1] * len(options)
        selected_option = random.choices(options, weights=weights)[0]
        safe_click(driver, selected_option)
        time.sleep(random.uniform(0.05, 0.15))


def multiple_choice(driver, num, weights=-1, min_choices=-1, max_choices=-1):
    questions = driver.find_elements(By.CSS_SELECTOR, f'#div{num} > div.ui-controlgroup')
    for question in questions:
        options = question.find_elements(By.CSS_SELECTOR, '.ui-checkbox')
        if not options:
            continue
        n = len(options)
        if weights == -1 or len(weights) != n:
            probs = [random.random() for _ in range(n)]
        else:
            max_weight = max((w for w in weights if w != -1), default=1)
            probs = [(w / max_weight if max_weight > 0 else 0) if w != -1 else random.random() for w in weights]
        selected_indices = {i for i, p in enumerate(probs) if random.random() < p}
        if not selected_indices:
            selected_indices.add(random.randint(0, n - 1))
        if min_choices != -1 and len(selected_indices) < min_choices:
            remaining = [i for i in range(n) if i not in selected_indices]
            needed = min_choices - len(selected_indices)
            selected_indices.update(random.sample(remaining, min(needed, len(remaining))))
        if max_choices != -1 and len(selected_indices) > max_choices:
            selected_indices = set(random.sample(list(selected_indices), max_choices))
        for idx in selected_indices:
            try:
                if not options[idx].is_selected():
                    safe_click(driver, options[idx])
                    time.sleep(random.uniform(0.1, 0.2))
            except Exception as e:
                print(f"点击多选选项出错: {e}")


def fill_in_the_blank(driver, num, answers, weights):
    selected_answer = random.choices(answers, weights=weights if weights != -1 else None)[0]
    driver.find_element(By.CSS_SELECTOR, f'#q{num}').send_keys(str(selected_answer))
    time.sleep(random.uniform(0.05, 0.15))


def matrix_question(driver, num, personality_type):
    rows = driver.find_elements(By.XPATH, f'//*[@id="div{num}"]/div[2]/table/tbody/tr')
    if not rows:
        return
    dominant_zone, last_selected, random_chance = None, None, 0.1
    for row in rows:
        options = row.find_elements(By.TAG_NAME, 'a')
        n_options = len(options)
        if not options:
            continue
        if dominant_zone is None:
            zones = {
                'high_score': range(n_options // 2, n_options),
                'mid_score': range(n_options // 3, n_options * 2 // 3),
                'low_score': range(0, n_options // 2)
            }
            chances = {'high_score': 0.02, 'mid_score': 0.05, 'low_score': 0.02, 'chaotic': 0.2}
            dominant_zone = random.choice(zones.get(personality_type, range(n_options)))
            random_chance = chances.get(personality_type, 0.2)
        if random.random() < random_chance or last_selected is None:
            weights = [0.1] * n_options
            weights[dominant_zone] = 0.7
            if dominant_zone > 0:
                weights[dominant_zone - 1] = 0.2
            if dominant_zone < n_options - 1:
                weights[dominant_zone + 1] = 0.2
            selected_index = random.choices(range(n_options), weights=weights)[0]
        else:
            candidate_indices = {max(0, last_selected - 1), last_selected, min(n_options - 1, last_selected + 1)}
            selected_index = random.choice(list(candidate_indices))
        safe_click(driver, options[selected_index])
        last_selected = selected_index
        time.sleep(random.uniform(0.1, 0.25))


def scale_question(driver, num, weights):
    options = driver.find_elements(By.XPATH, f'//*[@id="div{num}"]/div[2]/div/ul/li')
    if not options:
        return
    if weights == -1 or len(weights) != len(options):
        weights = [1] * len(options)
    selected_option = random.choices(options, weights=weights)[0]
    safe_click(driver, selected_option)
    time.sleep(random.uniform(0.05, 0.15))


def dropdown_question(driver, num, weights):
    dropdown_container = driver.find_element(By.XPATH, f'//*[@id="select2-q{num}-container"]')
    safe_click(driver, dropdown_container)
    options = WebDriverWait(driver, 5).until(
        EC.presence_of_all_elements_located((By.CSS_SELECTOR, '.select2-results__option'))
    )
    if not options:
        return
    if weights == -1 or len(weights) != len(options):
        weights = [1] * len(options)
    random_option = random.choices(options, weights=weights)[0]
    safe_click(driver, random_option)
    time.sleep(random.uniform(0.05, 0.15))


def yanzheng(driver):
    """验证弹窗处理"""
    try:
        confirm_button = WebDriverWait(driver, 0.5).until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, '.layui-layer-btn a.layui-layer-btn0')))
        safe_click(driver, confirm_button)
        print("已关闭提交确认弹窗。")
    except TimeoutException:
        pass
    try:
        verify_button = WebDriverWait(driver, 0.5).until(
            EC.element_to_be_clickable((By.ID, 'SM_BTN_1')))
        safe_click(driver, verify_button)
        print("已点击智能验证按钮。")
        time.sleep(1.5)
    except TimeoutException:
        pass
    try:
        slider_handle = driver.find_element(By.ID, 'nc_1_n1z')
        track = driver.find_element(By.ID, 'nc_1__scale_text')
        ActionChains(driver).drag_and_drop_by_offset(slider_handle, track.size['width'], 0).perform()
        print("已尝试拖动滑块。")
        time.sleep(1)
    except NoSuchElementException:
        pass
    except Exception as e:
        print(f"拖动滑块时发生错误: {e}")


# ---------------- 调用流程保持不变 ----------------

def qd(times):
    url = 'https://www.wjx.cn/vm/e8pnohT.aspx'
    driver_path = r"G:\msedgedriver.exe"
    options = webdriver.EdgeOptions()
    options.add_argument("--disable-blink-features=AutomationControlled")
    options.binary_location = r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"

    for i in range(times):
        service = Service(driver_path)
        driver = webdriver.Edge(service=service, options=options)
        try:
            driver.get(url)

            # 按题型调用（保持原调用流程）
            single_choice(driver, 1, [2, 1, 1, 1])  # 第1题单选：第1个选项权重2（概率最高）
            multiple_choice(driver, 2, [10, 20, 100, 0, 50])  # 第2题多选：第3个选项权重100（必选），第4个0（不选），如果weights=-1，则随机,默认min和max都是-1，即随机，表示最小和最大选项）
            fill_in_the_blank(driver, 3, [666, 111], [1, 0])  # 第3题填空：只能选666（第2个答案权重0）
            dropdown_question(driver, 4, [0, 50, 50])  # 第4题下拉：第2、3个选项各50（等概率）
            matrix_question(driver, 5, 'high_score')  # 第5题矩阵：倾向选择高分选项
            scale_question(driver, 6, [50, 10, 20, 20, 0])  # 第6题量表：第1个选项权重50（概率最高）

            safe_submit(driver)
            yanzheng(driver)
            time.sleep(1000) #设置间隔 括号内时间自己设置
            print(f'已经提交了{i + 1}次问卷')

        except Exception as e:
            print(f'出现异常: {str(e)}')
        finally:
            driver.quit()


if __name__ == "__main__":
    processes = []
    for i in range(1):  # 开几个浏览器窗口
        p = multiprocessing.Process(target=qd, args=(1,))  # 每个窗口跑几次
        processes.append(p)
        p.start()
    for p in processes:
        p.join()
